/*!
\copyright  zysx
*/

#include "zy_gaia.h"
#include "adi1787.h"
#include "headset_sm.h"
#include "voice_sources.h"
#include "zy_light_sensor.h"
#include "headset_test.h"
#include "pio_common.h"
#include "battery_monitor.h"
#include "handset_service.h"
#include "bdaddr.h"
#include "common_test.h"
#include "music_processing.h"
#include "kymera_music_processing.h"
#include "audio_config.h"
#include "byte_utils.h"
#include "ps.h"
#include "ui.h"
#include "ui_inputs.h"
#include "headset_test.h"

typedef enum
{
    USER_EQ_PARAM_HI_OFFSET,
    USER_EQ_PARAM_LO_OFFSET,
    USER_EQ_VALUE_HI_OFFSET,
    USER_EQ_VALUE_LO_OFFSET
} gaia_cmd_user_eq_param_payload_offset;

typedef enum
{
    user_eq_param_type_filter,
    user_eq_param_type_cutoff_freq,
    user_eq_param_type_gain,
    user_eq_param_type_q
} user_eq_param_type_t;

typedef enum
{
    opid_select                = (0x0),
    opid_up,
    opid_down,
    opid_left,
    opid_right,
    opid_right_up,
    opid_right_down,
    opid_left_up,
    opid_left_down,
    opid_root_menu,
    opid_setup_menu,
    opid_contents_menu,
    opid_favourite_menu,
    opid_exit,
    /* 0x0E to 0x1F Reserved */
    opid_0                    = (0x20),
    opid_1,
    opid_2,
    opid_3,
    opid_4,
    opid_5,
    opid_6,
    opid_7,
    opid_8,
    opid_9,
    opid_dot,
    opid_enter,
    opid_clear,
    /* 0x2D - 0x2F Reserved */
    opid_channel_up            = (0x30),
    opid_channel_down,
    opid_sound_select,
    opid_input_select,
    opid_display_information,
    opid_help,
    opid_page_up,
    opid_page_down,
    /* 0x39 - 0x3F Reserved */
    opid_power                = (0x40),
    opid_volume_up,
    opid_volume_down,
    opid_mute,
    opid_play,
    opid_stop,
    opid_pause,
    opid_record,
    opid_rewind,
    opid_fast_forward,
    opid_eject,
    opid_forward,
    opid_backward,
    /* 0x4D - 0x4F Reserved */
    opid_angle                = (0x50),
    opid_subpicture,
    /* 0x52 - 0x70 Reserved */
    opid_f1                    = (0x71),
    opid_f2,
    opid_f3,
    opid_f4,
    opid_f5,
    opid_vendor_unique        = (0x7E)
    /* Ox7F Reserved */
} avc_operation_id; 


typedef struct __sink_gaia_global_data_t
{
    GAIA_TRANSPORT *gaia_transport;

    pio_common_allbits pio_change_mask;
    pio_common_allbits pio_old_state;
    ringtone_note *alert_tone;

    unsigned notify_ui_event:1;
    unsigned notify_charger_connection:1;
    unsigned notify_battery_charged:1;
    unsigned notify_speech_rec:1;
    unsigned dfu_boot_status:2;
    unsigned num_mapped_pios:6;
    unsigned activate_peq_done:1;
    unsigned gaia_remain_connected:1;
    unsigned gatt_upgrade_in_progress:1;
    unsigned unused:1;
}zy_gaia_global_data_t;
static zy_gaia_global_data_t gaia_gdata;
static bool volume_is_muted = FALSE;
uint16 eq_ucid = 0;
uint32 my_peq_gains[10];
#define GAIA_GDATA gaia_gdata
#define HIGH(x) ((x) >> 8)
#define LOW(x) ((x) & 0xFF)

void gaia_set_transport(GAIA_TRANSPORT* transport)
{
    GAIA_GDATA.gaia_transport = transport;
}

GAIA_TRANSPORT* gaia_get_transport(void)
{
    return GAIA_GDATA.gaia_transport;
}

/* 储存用户自动义PEQ gain值，放在数组1-6(其余参数无效) */
void zy_set_user_eq_ucid(uint16 eq_ucid)
{
    if (eq_ucid == 1) //V2版本默认ucid 1为用户EQ
    {
        Kymera_SelectEqBankNow(EQ_BANK_USER);
    }else if(eq_ucid == 0)
    {
        Kymera_SelectEqBankNow(0);
    }else
    {
        Kymera_SelectEqBankNow(eq_ucid - 1); //减去后置的USER EQ BANK
    }
}

static audio_plugin_user_eq_param_id_t get_param_id_from_payload(uint8* payload)
{
    audio_plugin_user_eq_param_id_t param_id;
    uint16 id = MAKEWORD_HI_LO(payload[USER_EQ_PARAM_HI_OFFSET], payload[USER_EQ_PARAM_LO_OFFSET]);
    param_id.bank = (id >> 8) & 0x000F;
    param_id.band = (id >> 4) & 0x000F;
    param_id.param_type = id & 0x000F;
    return param_id;
}

static uint32 convertValueTo32Bit(const uint16 value, const bool valueIsSigned)
{
    if (valueIsSigned)
    {
        if (value >= 0x8000)
            return (uint32)(value - 0x10000);
        else
            return (uint32)value;
    }
    else
    {
        return value;
    }
}

static uint16 get_param_value_from_payload(uint8* payload)
{
    return MAKEWORD_HI_LO(payload[USER_EQ_VALUE_HI_OFFSET], payload[USER_EQ_VALUE_LO_OFFSET]);
}

static bool isMasterGainSelected(const uint16 band, const eq_param_type_t param_type)
{
    return ((band == 0) && (param_type == user_eq_param_type_cutoff_freq));
}

static bool saveUserPeqToPskey(void)
{
    uint16 high_16, low_16;
    uint16 peq_gain_buffer[20];
    PsStore(eq_ucid_key, (const uint16 *)&eq_ucid, PS_SIZE_ADJ(sizeof(uint16)));
    for (int i = 0; i < 10; i++)
    {
        low_16 = (uint16)(my_peq_gains[i]) & 0xffff;
        high_16 = (uint16)(my_peq_gains[i] >> 16) & 0xffff;
        peq_gain_buffer[i * 2] = high_16;
        peq_gain_buffer[i * 2 + 1] = low_16;
    }
    PsStore(my_peq_gain_key, (const uint16 *)peq_gain_buffer, 20);
    return TRUE;   
}

static bool isParamValueSigned(const audio_plugin_user_eq_param_id_t* param_id)
{
    if (isMasterGainSelected(param_id->band, param_id->param_type))
    {
        return TRUE;
    }
    else
    {
        switch (param_id->param_type)
        {
            case eq_param_type_gain:
                /* gain is a signed value so must take care when changing from 16 to 32 bits */
                return TRUE;

            case eq_param_type_filter:
            case eq_param_type_cutoff_freq:
            case eq_param_type_q:
            default:
                return FALSE;
        }
    }
}

static bool app_event_from_opid(avc_operation_id opid)
{
    static int current_volume = 1;
    switch (opid)
    {
    case opid_power:
        return TRUE;

    case opid_volume_up:
        Ui_InjectUiInput(ui_input_volume_up);
        return TRUE;

    case opid_volume_down:
        Ui_InjectUiInput(ui_input_volume_down);
        return TRUE;

    case opid_mute:
        if (volume_is_muted)
        {
            volume_is_muted = FALSE;
            appTestAvVolumeSet(current_volume);
        }else
        {
            current_volume = appTestGetCurrentAudioVolume();
            volume_is_muted = TRUE;
            appTestAvVolumeSet(0);
        }
        return TRUE;

    case opid_play:
        Ui_InjectUiInput(ui_input_play);
        return TRUE;

    case opid_stop:
        Ui_InjectUiInput(ui_input_stop_av_connection);
        return TRUE;

    case opid_pause:
        Ui_InjectUiInput(ui_input_pause_all);
        return TRUE;

    case opid_forward:
        Ui_InjectUiInput(ui_input_av_forward);
        return TRUE;

    case opid_backward:
        Ui_InjectUiInput(ui_input_av_backward);
        return TRUE;

    default:
        return FALSE;
    }
}

static void handle_zy_gaia_command_control(GAIA_TRANSPORT *transport, uint16 vendor_id, uint16 command_id, uint16 payload_size, const uint8 *payload)
{
    uint8 response[1];
    uint8 my_response[40];
    uint32 gain;
    switch (command_id)
    {
    case GAIA_COMMAND_GET_ANC_STATE:
        response[0] = anc_state;
        GaiaSendPacket(transport, vendor_id, command_id | GAIA_ACK_MASK, GAIA_STATUS_SUCCESS, 1, response);
        break;
    
    case GAIA_COMMAND_SET_ANC_STATE:
        if (appTestIsHandsetHfpScoActive())
        {
            play_tone_on_anc_changing = 0;
        }else
        {
            play_tone_on_anc_changing = 1;
        }
        MessageSend(AncGetTask(), payload[0], 0);
        GaiaSendPacket(transport, vendor_id, command_id | GAIA_ACK_MASK, GAIA_STATUS_SUCCESS, 0, NULL);
        break;

    case GAIA_COMMAND_SET_ANC_ON_STATE:    /* liujin set ANC ON state command */
        if (payload[0] == AncOnStateHigh)
        {
            MessageCancelAll(AncAdaptiveGetTask(), EventAncAdaptiveSetLevelLow);
            MessageCancelAll(AncAdaptiveGetTask(), EventAncAdaptiveSetLevelMid);
            MessageCancelAll(AncAdaptiveGetTask(), EventAncAdaptiveSetLevelHigh);
            if (anc_adaptive_enabled)
            {
                MessageSend(AncAdaptiveGetTask(), EventAncAdaptiveStop, 0);
            }
            MessageSendLater(AncAdaptiveGetTask(), EventAncAdaptiveSetLevelHigh, 0, 200);
        }else if (payload[0] == AncOnStateMid)
        {
            MessageCancelAll(AncAdaptiveGetTask(), EventAncAdaptiveSetLevelLow);
            MessageCancelAll(AncAdaptiveGetTask(), EventAncAdaptiveSetLevelMid);
            MessageCancelAll(AncAdaptiveGetTask(), EventAncAdaptiveSetLevelHigh);
            if (anc_adaptive_enabled)
            {
                MessageSend(AncAdaptiveGetTask(), EventAncAdaptiveStop, 0);
            }
            MessageSendLater(AncAdaptiveGetTask(), EventAncAdaptiveSetLevelMid, 0, 200);
        }
        else if (payload[0] == AncOnStateLow)
        {
            MessageCancelAll(AncAdaptiveGetTask(), EventAncAdaptiveSetLevelLow);
            MessageCancelAll(AncAdaptiveGetTask(), EventAncAdaptiveSetLevelMid);
            MessageCancelAll(AncAdaptiveGetTask(), EventAncAdaptiveSetLevelHigh);
            if (anc_adaptive_enabled)
            {
                MessageSend(AncAdaptiveGetTask(), EventAncAdaptiveStop, 0);
            }
            MessageSendLater(AncAdaptiveGetTask(), EventAncAdaptiveSetLevelLow, 0, 200);
        }
        else
        {
            MessageSend(AncAdaptiveGetTask(), EventAncAdaptiveStart, 0);
        }
        GaiaSendPacket(transport, vendor_id, command_id | GAIA_ACK_MASK, GAIA_STATUS_SUCCESS, 0, NULL);
        break;

    case GAIA_COMMAND_GET_ANC_ON_STATE:
        response[0] = (uint8)(anc_on_state | (anc_adaptive_enabled << 4) | (ANC_ADAPTIVE_SUPPORTED << 7));
        GaiaSendPacket(transport, vendor_id, command_id | GAIA_ACK_MASK, GAIA_STATUS_SUCCESS, 1, response);
        break;
    
    case GAIA_COMMAND_SET_WEAR_DETECTION:  /* liujin wear decetor control command */
        if (payload[0] == 1)
        {
            set_light_sensor_enabled(TRUE);
        }else
        {
            set_light_sensor_enabled(FALSE);
        }
        GaiaSendPacket(transport, vendor_id, command_id | GAIA_ACK_MASK, GAIA_STATUS_SUCCESS, 0, NULL);
        break;

    case GAIA_COMMAND_GET_WEAR_DETECTION:
        response[0] = get_light_sensor_enabled();
        GaiaSendPacket(transport, vendor_id, command_id | GAIA_ACK_MASK, GAIA_STATUS_SUCCESS, 1, response);
        break;

    case GAIA_COMMAND_SET_EQ_CONTROL:
        if ((payload_size == 1) && (payload[0] <= 6))
        {
            eq_ucid = payload[0];
            PsStore(eq_ucid_key, (const uint16 *)&eq_ucid, PS_SIZE_ADJ(sizeof(uint16)));
            zy_set_user_eq_ucid(eq_ucid);
            GaiaSendPacket(transport, vendor_id, command_id | GAIA_ACK_MASK, GAIA_STATUS_SUCCESS, 0, NULL);
        }
        break;

    case GAIA_COMMAND_GET_EQ_CONTROL:
        response[0]  = LOW(eq_ucid);
        GaiaSendPacket(transport, vendor_id, command_id | GAIA_ACK_MASK, GAIA_STATUS_SUCCESS, 1, response);
        break;

    case GAIA_COMMAND_SET_USER_EQ_PARAMETER:
        if(payload_size == 5)
        {
            audio_plugin_user_eq_param_t param;
            uint8 payload_t[5];
            int16 gain_int16;
            for (uint8 i = 0; i < 5; i++)
            {
                payload_t[i] = payload[i];
            }
            param.id = get_param_id_from_payload(payload_t);
            param.value = convertValueTo32Bit(get_param_value_from_payload(payload_t), isParamValueSigned(&param.id));
            if (param.id.param_type == eq_param_type_gain)
            {
                my_peq_gains[param.id.band] = param.value;
                gain_int16 = (int16)param.value;
                DEBUG_LOG_INFO("set peq gain band: %d value:%d ", param.id.band, gain_int16);
                Kymera_SetUserEqBandsNow(param.id.band-1, param.id.band-1, &gain_int16);
                saveUserPeqToPskey();
            }
        }
        break;
    
    case GAIA_COMMAND_GET_MY_PEQ_GAIN:
        for (int i = 0; i < 10; i++)
        {
            gain = my_peq_gains[i];
            my_response[i*4] = (uint8) (gain>>24)&0xff;
            my_response[i*4+1] = (uint8) (gain>>16)&0xff;
            my_response[i*4+2] = (uint8) (gain>>8)&0xff;
            my_response[i*4+3] = (uint8) gain&0xff;
        }
        GaiaSendPacket(transport, vendor_id, command_id | GAIA_ACK_MASK, GAIA_STATUS_SUCCESS, 40, my_response);
        break;

    case GAIA_COMMAND_SAVE_EQ_PARAM:  /* liujin 保存peq gain到pskey */
        if (saveUserPeqToPskey())
        {
            GaiaSendPacket(transport, vendor_id, command_id | GAIA_ACK_MASK, GAIA_STATUS_SUCCESS, 0, NULL);
        }
        break;

    case GAIA_COMMAND_AV_REMOTE_CONTROL:
        if (payload_size == 0)
        {
           GaiaSendPacket(transport, vendor_id, command_id | GAIA_ACK_MASK, GAIA_STATUS_SUCCESS, 0, NULL);
        }else
        {
            if(app_event_from_opid(payload[0]))
            {
                GaiaSendPacket(transport, vendor_id, command_id | GAIA_ACK_MASK, GAIA_STATUS_SUCCESS, 0, NULL);
            }else
            {
                GaiaSendPacket(transport, vendor_id, command_id | GAIA_ACK_MASK, GAIA_STATUS_NOT_SUPPORTED, 0, NULL);
            }
        }
        break;

    case GAIA_COMMAND_VOLUME_GET_MUTE_STATE:
        if (appTestGetCurrentAudioVolume() == 0)
        {
            response[0]  = 1;
        }else
        {
            response[0]  = 0;
        }
        GaiaSendPacket(transport, vendor_id, command_id | GAIA_ACK_MASK, GAIA_STATUS_SUCCESS, 1, response);
        break;

    default:
        break;
    }
}

static void handle_zy_gaia_command_status(GAIA_TRANSPORT *transport, uint16 vendor_id, uint16 command_id, uint16 payload_size, const uint8 *payload)
{
    UNUSED(payload_size);
    UNUSED(payload);
    uint8 response[3];
    uint16 voltage;
    tp_bdaddr tp_addr;
    switch (command_id)
    {
    case GAIA_COMMAND_GET_API_VERSION:
        response[0] = 1;
        response[1] = 2;
        response[2] = 0;
        GaiaSendPacket(transport, vendor_id, command_id | GAIA_ACK_MASK, GAIA_STATUS_SUCCESS, 3, response);
        break;
    case GAIA_COMMAND_GET_CURRENT_RSSI:
        if(HandsetService_GetConnectedBredrHandsetTpAddress(&tp_addr))
        {
            voltage =  appTestGetRssiOfTpAddr(&tp_addr);
            response[0] = LOW(voltage);
            GaiaSendPacket(transport, vendor_id, command_id | GAIA_ACK_MASK, GAIA_STATUS_SUCCESS, 1, response);
        }
        break;
    case GAIA_COMMAND_GET_CURRENT_BATTERY_LEVEL:
        voltage = appBatteryGetVoltageAverage();
        response[0] = HIGH(voltage);
        response[1] = LOW(voltage);
        GaiaSendPacket(transport, vendor_id, command_id | GAIA_ACK_MASK, GAIA_STATUS_SUCCESS, 2, response);
        break;
    
    default:
        break;
    }
}

void gaiaFrameworkCommand_GetZyCommand(GAIA_TRANSPORT *transport, uint16 vendor_id, uint16 command_id, uint16 payload_size, const uint8 *payload)
{
    DEBUG_LOG_INFO("GetZyCommand: command_id %04X ", command_id);
    uint16 command_type = command_id & GAIA_COMMAND_TYPE_MASK;
    if (command_type == GAIA_COMMAND_TYPE_CONTROL)
    {
        handle_zy_gaia_command_control(transport, vendor_id, command_id, payload_size, payload);
    }else if (command_type == GAIA_COMMAND_TYPE_STATUS)
    {
        handle_zy_gaia_command_status(transport, vendor_id, command_id, payload_size, payload);
    }else
    {
        DEBUG_LOG_INFO("not report command: command_id %04X ", command_id);
    }
    
}
